package com.vv.tools.modules.auth.filter;

import com.vv.tools.common.util.TokenUtil;
import com.vv.tools.modules.auth.config.AuthUrlConfig;
import com.vv.tools.modules.auth.service.UserAuthService;

import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.List;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * @author wen.li
 * token解析 & 授权
 */
@Slf4j
@Component("checkTokenFilter")
public class CheckTokenFilter extends OncePerRequestFilter {

    private static final AntPathMatcher MATCHER = new AntPathMatcher();

    @Resource
    private UserAuthService userAuthService;

    @Resource
    private AuthUrlConfig authUrlConfig;

    @Override
    protected void doFilterInternal(@Nullable HttpServletRequest request,
                                    @Nullable HttpServletResponse response,
                                    @Nullable FilterChain filterChain) throws ServletException, IOException {
        assert request != null;
        assert filterChain != null;
        Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
        if (existingAuth != null && existingAuth.isAuthenticated()) {
            // 已经认证，跳过 JWT 解析
            filterChain.doFilter(request, response);
            return;
        }

        log.info("checkTokenFilter::start");
        validateToken(request);
        log.info("checkTokenFilter::end");
        filterChain.doFilter(request, response);
    }

    //token验证
    private void validateToken(HttpServletRequest request) {
        //从请求的头部获取token
        String authorization = request.getHeader("authorization");
        //如果请求头部没有获取到token，则从请求参数中获取token
        if (StringUtils.isEmpty(authorization)) {
            log.info("checkTokenFilter::token is null");
            return;
        }
        String token = authorization.substring("Bearer ".length());
        if (!TokenUtil.validateToken(token)) {
            throw new AccountExpiredException("无效的认证");
        }

        // token 过期
        if (TokenUtil.isExpiry(token)) {
            throw new AccountExpiredException("认证已过期");
        }

        //解析token
        String username = TokenUtil.extractUsername(token);
        if (StringUtils.isEmpty(username)) {
            throw new UsernameNotFoundException("用户不存在");
        }

        //获取用户信息
        UserDetails userDetails = userAuthService.loadUserByUsername(username);
        if (userDetails == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
            userDetails, null, userDetails.getAuthorities()
        );
        //设置到spring security上下文
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        // 可选：忽略某些路径，如登录接口
        return matches(request.getRequestURI(), authUrlConfig.getPermit());
    }

    private boolean matches(String path, List<String> patterns) {
        if (ObjectUtils.isEmpty(path) || CollectionUtils.isEmpty(patterns)) {
            return false;
        }
        for (String pattern : patterns) {
            if (isMatch(pattern, path)) {
                return true;
            }
        }
        return false;
    }

    private boolean isMatch(String pattern, String path) {
        return MATCHER.match(pattern, path);
    }

}
